﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace SimulationPresenter
{
    public partial class DisplayForm : Form
    {
        private MainForm parentForm;
        public Device device;

        public DisplayForm()
        {
            InitializeComponent(); 
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
        }

        public DisplayForm(MainForm parentForm)
        {
            InitializeComponent();
            SetupCamera();
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
            this.parentForm = parentForm;
        }

        public void SetupCamera()
        {
            ResetWholeView();
        }

        protected override void OnShown(EventArgs e)
        {
            PresentParameters presentParams = new PresentParameters();
            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.EnableAutoDepthStencil = true;
            
            device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            device.Transform.Projection = Matrix.PerspectiveFovLH(ViewCone , Width / Height, 1f, 10000f);
            device.Transform.View = Matrix.LookAtLH(CameraPosition, LookAtPoint, LookUpVector);
            device.RenderState.Lighting = true;
            device.RenderState.CullMode = Cull.None;
            AddLights(device);
            device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0);

            device.BeginScene();

            //Generate display
            if (null != parentForm)
            {
                parentForm.DrawScene(device); 
            }
            device.EndScene();

            device.Present();
            Invalidate();
        }

        private void AddLights(Device device)
        {
            device.Lights[0].Type = LightType.Directional;
            device.Lights[0].Diffuse = Color.White;
            device.Lights[0].Direction = new Vector3(0, -1, 0);
            device.Lights[0].Range = 200f;
            device.Lights[0].Specular = Color.White;
            device.Lights[0].Enabled = true;
            device.Lights[1].Type = LightType.Directional;
            device.Lights[1].Diffuse = Color.White;
            device.Lights[1].Direction = new Vector3(-1, -1, 0);
            device.Lights[1].Enabled = true;
            device.Lights[1].Type = LightType.Point;
            device.Lights[1].Diffuse = Color.White;
            device.Lights[1].Range = 30.0f;
            device.Lights[1].Position = new Vector3(-5, 10, 0);
            device.Lights[1].Enabled = true;
        }

        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);
            if (null != parentForm)
            {
                parentForm.Close();
            }
        }

        internal void KeyDownProcess(Keys keys)
        {
            switch (keys)
            {
                case Keys.KeyCode:
                    break;
                case Keys.Modifiers:
                    break;
                case Keys.None:
                    break;
                case Keys.LButton:
                    break;
                case Keys.RButton:
                    break;
                case Keys.Cancel:
                    break;
                case Keys.MButton:
                    break;
                case Keys.XButton1:
                    break;
                case Keys.XButton2:
                    break;
                case Keys.Back:
                    ResetWholeView();
                    break;
                case Keys.Tab:
                    break;
                case Keys.LineFeed:
                    break;
                case Keys.Clear:
                    break;
                case Keys.Return:
                    break;
                case Keys.ShiftKey:
                    break;
                case Keys.ControlKey:
                    break;
                case Keys.Menu:
                    break;
                case Keys.Pause:
                    break;
                case Keys.Capital:
                    break;
                case Keys.KanaMode:
                    break;
                case Keys.JunjaMode:
                    break;
                case Keys.FinalMode:
                    break;
                case Keys.HanjaMode:
                    break;
                case Keys.Escape:
                    break;
                case Keys.IMEConvert:
                    break;
                case Keys.IMENonconvert:
                    break;
                case Keys.IMEAccept:
                    break;
                case Keys.IMEModeChange:
                    break;
                case Keys.Space:
                    break;
                case Keys.Prior:
                    break;
                case Keys.Next:
                    break;
                case Keys.End:
                    ZoomOut();
                    break;
                case Keys.Home:
                    ZoomIn();
                    break;
                case Keys.Left:
                    RotateLeft();
                    break;
                case Keys.Up:
                    RotateUp();
                    break;
                case Keys.Right:
                    RotateRight();
                    break;
                case Keys.Down:
                    RotateDown();
                    break;
                case Keys.Select:
                    break;
                case Keys.Print:
                    break;
                case Keys.Execute:
                    break;
                case Keys.Snapshot:
                    break;
                case Keys.Insert:
                    break;
                case Keys.Delete:
                    break;
                case Keys.Help:
                    break;
                case Keys.D0:
                    break;
                case Keys.D1:
                    break;
                case Keys.D2:
                    break;
                case Keys.D3:
                    break;
                case Keys.D4:
                    break;
                case Keys.D5:
                    break;
                case Keys.D6:
                    break;
                case Keys.D7:
                    break;
                case Keys.D8:
                    break;
                case Keys.D9:
                    break;
                case Keys.A:
                    MoveLeft();
                    break;
                case Keys.B:
                    break;
                case Keys.C:
                    break;
                case Keys.D:
                    MoveRight();
                    break;
                case Keys.E:
                    RotateRight();
                    break;
                case Keys.F:
                    break;
                case Keys.G:
                    break;
                case Keys.H:
                    break;
                case Keys.I:
                    break;
                case Keys.J:
                    break;
                case Keys.K:
                    break;
                case Keys.L:
                    break;
                case Keys.M:
                    break;
                case Keys.N:
                    break;
                case Keys.O:
                    break;
                case Keys.P:
                    break;
                case Keys.Q:
                    RotateLeft();
                    break;
                case Keys.R:
                    break;
                case Keys.S:
                    MoveBackward();
                    break;
                case Keys.T:
                    break;
                case Keys.U:
                    break;
                case Keys.V:
                    break;
                case Keys.W:
                    MoveForward();
                    break;
                case Keys.X:
                    break;
                case Keys.Y:
                    break;
                case Keys.Z:
                    break;
                case Keys.LWin:
                    break;
                case Keys.RWin:
                    break;
                case Keys.Apps:
                    break;
                case Keys.Sleep:
                    break;
                case Keys.NumPad0:
                    break;
                case Keys.NumPad1:
                    break;
                case Keys.NumPad2:
                    break;
                case Keys.NumPad3:
                    break;
                case Keys.NumPad4:
                    break;
                case Keys.NumPad5:
                    break;
                case Keys.NumPad6:
                    break;
                case Keys.NumPad7:
                    break;
                case Keys.NumPad8:
                    break;
                case Keys.NumPad9:
                    break;
                case Keys.Multiply:
                    break;
                case Keys.Add:
                    break;
                case Keys.Separator:
                    break;
                case Keys.Subtract:
                    break;
                case Keys.Decimal:
                    break;
                case Keys.Divide:
                    break;
                case Keys.F1:
                    break;
                case Keys.F2:
                    break;
                case Keys.F3:
                    break;
                case Keys.F4:
                    break;
                case Keys.F5:
                    break;
                case Keys.F6:
                    break;
                case Keys.F7:
                    break;
                case Keys.F8:
                    break;
                case Keys.F9:
                    break;
                case Keys.F10:
                    break;
                case Keys.F11:
                    break;
                case Keys.F12:
                    break;
                case Keys.F13:
                    break;
                case Keys.F14:
                    break;
                case Keys.F15:
                    break;
                case Keys.F16:
                    break;
                case Keys.F17:
                    break;
                case Keys.F18:
                    break;
                case Keys.F19:
                    break;
                case Keys.F20:
                    break;
                case Keys.F21:
                    break;
                case Keys.F22:
                    break;
                case Keys.F23:
                    break;
                case Keys.F24:
                    break;
                case Keys.NumLock:
                    break;
                case Keys.Scroll:
                    break;
                case Keys.LShiftKey:
                    break;
                case Keys.RShiftKey:
                    break;
                case Keys.LControlKey:
                    break;
                case Keys.RControlKey:
                    break;
                case Keys.LMenu:
                    break;
                case Keys.RMenu:
                    break;
                case Keys.BrowserBack:
                    break;
                case Keys.BrowserForward:
                    break;
                case Keys.BrowserRefresh:
                    break;
                case Keys.BrowserStop:
                    break;
                case Keys.BrowserSearch:
                    break;
                case Keys.BrowserFavorites:
                    break;
                case Keys.BrowserHome:
                    break;
                case Keys.VolumeMute:
                    break;
                case Keys.VolumeDown:
                    break;
                case Keys.VolumeUp:
                    break;
                case Keys.MediaNextTrack:
                    break;
                case Keys.MediaPreviousTrack:
                    break;
                case Keys.MediaStop:
                    break;
                case Keys.MediaPlayPause:
                    break;
                case Keys.LaunchMail:
                    break;
                case Keys.SelectMedia:
                    break;
                case Keys.LaunchApplication1:
                    break;
                case Keys.LaunchApplication2:
                    break;
                case Keys.OemSemicolon:
                    break;
                case Keys.Oemplus:
                    ResetZoom();
                    break;
                case Keys.Oemcomma:
                    break;
                case Keys.OemMinus:
                    ResetCamera();
                    break;
                case Keys.OemPeriod:
                    break;
                case Keys.OemQuestion:
                    break;
                case Keys.Oemtilde:
                    break;
                case Keys.OemOpenBrackets:
                    break;
                case Keys.OemPipe:
                    break;
                case Keys.OemCloseBrackets:
                    break;
                case Keys.OemQuotes:
                    break;
                case Keys.Oem8:
                    break;
                case Keys.OemBackslash:
                    break;
                case Keys.ProcessKey:
                    break;
                case Keys.Packet:
                    break;
                case Keys.Attn:
                    break;
                case Keys.Crsel:
                    break;
                case Keys.Exsel:
                    break;
                case Keys.EraseEof:
                    break;
                case Keys.Play:
                    break;
                case Keys.Zoom:
                    break;
                case Keys.NoName:
                    break;
                case Keys.Pa1:
                    break;
                case Keys.OemClear:
                    break;
                case Keys.Shift:
                    break;
                case Keys.Control:
                    break;
                case Keys.Alt:
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        protected override void OnKeyDown(KeyEventArgs e)
        {
            KeyDownProcess(e.KeyCode);
        }

        private void ResetCamera()
        {
            CameraPosition = CameraPositionDefault;
            LookAtPoint = LookAtPointDefault;
            LookUpVector = LookUpVectorDefault;
        }

        private void ResetZoom()
        {
            ViewCone = ViewConeDefault;
        }

        private void ResetWholeView()
        {
            ResetCamera();
            ResetZoom();
        }

        private void RotateDown()
        {
            var rotationMatrix = Matrix.RotationAxis(GetUpDownRotationAxis(), -CameraRotationStep);

            CameraPosition.TransformCoordinate(rotationMatrix);
            LookUpVector.TransformCoordinate(rotationMatrix);
        }

        private Vector3 GetUpDownRotationAxis()
        {
            var moveDirection = CameraPosition - LookAtPoint;
            var moveDirectionNormalized = moveDirection;
            moveDirectionNormalized.Normalize();

            return Vector3.Cross(moveDirectionNormalized, LookUpVector);
        }

        private void RotateUp()
        {
            var rotationMatrix = Matrix.RotationAxis(GetUpDownRotationAxis(), CameraRotationStep);

            CameraPosition.TransformCoordinate(rotationMatrix);
            LookUpVector.TransformCoordinate(rotationMatrix);
        }

        private void RotateRight()
        {
            var moveDirection = CameraPosition - LookAtPoint;

            moveDirection.TransformCoordinate(Matrix.RotationAxis(LookUpVector, -CameraRotationStep));

            moveDirection.Add(LookAtPoint);

            CameraPosition = moveDirection;
        }

        private void RotateLeft()
        {
            var moveDirection = CameraPosition - LookAtPoint;

            moveDirection.TransformCoordinate(Matrix.RotationAxis(LookUpVector, CameraRotationStep));

            moveDirection.Add(LookAtPoint);

            CameraPosition = moveDirection;
        }

        private void ZoomIn()
        {
            if (ViewCone - ViewConeStep >= 0)
            {
                ViewCone -= ViewConeStep;
            }
        }

        private void ZoomOut()
        {
            if (ViewCone + ViewConeStep <= Math.PI)
            {
                ViewCone += ViewConeStep;
            }
        }

        private void MoveRight()
        {
            var sideMoveDirection = GetSideMoveDirection();

            sideMoveDirection.Scale(CameraPositionStep);

            CameraPosition += sideMoveDirection;
            LookAtPoint += sideMoveDirection;
        }

        private void MoveLeft()
        {
            var sideMoveDirection = GetSideMoveDirection();

            sideMoveDirection.Scale(CameraPositionStep);

            CameraPosition -= sideMoveDirection;
            LookAtPoint -= sideMoveDirection;
        }

        private void MoveBackward()
        {
            var moveDirection = GetMoveDirection();
            moveDirection.Scale(CameraPositionStep);

            CameraPosition -= moveDirection;
        }

        private void MoveForward()
        {
            var moveDirection = GetMoveDirection();
            moveDirection.Scale(CameraPositionStep);

            CameraPosition += moveDirection;
        }

        private Vector3 GetMoveDirection()
        {
            var moveDirection = LookAtPointDefault - CameraPosition;

            moveDirection.Normalize();
            return moveDirection;
        }

        private Vector3 GetSideMoveDirection()
        {
            var moveDirection = GetMoveDirection();

            moveDirection.TransformCoordinate(Matrix.RotationAxis(LookUpVector, (float)Math.PI / 2));

            return moveDirection;
        }

        /// <summary>
        /// View Cone size.
        /// </summary>
        private float ViewCone;

        /// <summary>
        /// Position of a camera.
        /// </summary>
        private Vector3 CameraPosition;

        private Vector3 LookAtPoint;

        private Vector3 LookUpVector;

        /// <summary>
        /// Default View Cone size.
        /// </summary>
        private readonly float ViewConeDefault = (float)Math.PI / 3;
        /// <summary>
        /// View Cone step. Equals 10 degrees
        /// </summary>
        private readonly float ViewConeStep = (float)Math.PI / 18;
        
        private readonly Vector3 CameraPositionDefault = new Vector3(50, 0, 50);

        private readonly float CameraPositionStep = 1.0f;

        private readonly Vector3 LookAtPointDefault = new Vector3(0, 0, 0);

        private readonly Vector3 LookUpVectorDefault = new Vector3(0, 1, 0); //new Vector3(-0.6f, -0.2f, -0.7f);

        private readonly float CameraRotationStep = (float)Math.PI / 18;

    }
}
